'use strict';
const fs = require('fs')
const path = require('path')
const userHome = require('user-home')
const fse = require('fs-extra')
const semver = require('semver')
const kebab = require('kebab-case')

const inquirer = require('inquirer')
const Command = require('@fs-dev-cli/command')
const log = require('@fs-dev-cli/log')
const Package = require('@fs-dev-cli/package')
const { spinnerStart, sleep, execPromise } = require('@fs-dev-cli/utils')
const getTemplate = require('./getTemplate');
const { version } = require('os');


const TYPE_PROJECT = 'project'
const TYPE_COMPONENT = 'component'

const TEMPLATE_TYPE_NORMAL = 'normal'
const TEMPLATE_TYPE_CUSTOM = 'custom'

const WHITE_COMMAND = ['node', 'npm', 'cnpm']
class InitCommand extends Command {
    init() {
        this.projectName = this._argv[0] || ''
        this.force = !!this._cmd._optionValues.force
        log.verbose('projectName', this.projectName)
        log.verbose('force', this.force)
    }

    async exec() {
        try {
            // 1、准备阶段
            this.projectInfo = await this.prepare()
            if (this.projectInfo) {
                log.verbose('projectInfo：', this.projectInfo)

                // 2、下载模板
                await this.downloadTemplate()

                // 3、安装模板
                await this.installTemplate()
            }


        } catch (error) {
            log.error(error.message)
        }

    }
    async installTemplate() {
        if (this.templateInfo) {
            if (this.templateInfo.type) {
                this.templateInfo.type = TEMPLATE_TYPE_NORMAL
            }
            if (this.templateInfo.type === TEMPLATE_TYPE_NORMAL) {
                // 标准安装
                await this.installNormalTemplate()
            } else if (this.templateInfo.type === TEMPLATE_TYPE_CUSTOM) {
                // 自定义安装
                await this.installCustomTemplate()
            } else {
                throw new Error('无法识别项目模板类型')
            }
        } else {
            throw new Error('项目模板信息不存在')
        }
    }

    checkCommand(cmd) {
        if (WHITE_COMMAND.includes(cmd)) {
            return cmd
        }
        return null
    }

    async execCommand(command, failMsg, successMsg) {
        let ret
        if (command) {
            const cmdArr = command.split(' ')
            const cmd = this.checkCommand(cmdArr[0])
            const args = cmdArr.slice(1)
            ret = await execPromise(cmd, args, {
                stdio: 'inherit',
                cwd: process.cwd()
            })
            if (ret !== 0) {
                throw new Error(failMsg)
            } else {
                log.success(successMsg)
            }
            return
        }
    }

    async installNormalTemplate() {
        const templatePath = path.resolve(this.templateNpm.cacheFilePath, 'template')
        const targetPath = process.cwd()
        const spinner = spinnerStart('正在安装模板...')
        await sleep(2000)
        try {
            fse.ensureDirSync(templatePath)
            fse.ensureDirSync(targetPath)
            fse.copySync(templatePath, targetPath)
        } catch (error) {
            log.error('安装模板失败')
            throw new Error(error)
        } finally {
            spinner.stop(true)
            log.success('安装模板成功')
        }
        const { installCommand, startCommand } = this.templateInfo

        await this.execCommand(installCommand, '依赖安装失败', '依赖安装成功')
        await this.execCommand(startCommand, '启动项目失败', '项目启动成功')

    }
    async installCustomTemplate() {
        console.log('安装自定义模板');
    }
    async downloadTemplate() {
        // 通过项目模板API获取项目模板信息
        const targetPath = path.resolve(userHome, '.fs-cli-dev', 'template')
        const storeDir = path.resolve(userHome, '.fs-cli-dev', 'template', 'node_modules')
        const { projectName, projectVersion, projectTemplate } = this.projectInfo
        this.templateInfo = this.template.find(e => e.npmName === projectTemplate)
        const { version, npmName } = this.templateInfo

        const templateNpm = new Package({
            targetPath,
            storeDir,
            packageName: npmName,
            packageVersion: version
        })
        log.verbose(targetPath, storeDir, npmName, version);
        // 不存在安装，存在更新
        if (!(await templateNpm.exists())) {
            const spinner = spinnerStart(`正在下载 ${projectTemplate}${version} 模板...'`)
            await sleep(2000)
            try {
                await templateNpm.install()
            } catch (error) {
                log.error('下载模板失败')
                throw new Error(error)
            } finally {
                spinner.stop(true)
                if (await templateNpm.exists()) {
                    log.success('下载模板成功')
                    this.templateNpm = templateNpm
                }

            }
        } else {
            const spinner = spinnerStart('正在更新模板...')
            try {
                await templateNpm.update()

            } catch (error) {
                log.error('跟新模板失败')
                throw new Error(error)
            } finally {
                spinner.stop(true)
                if (await templateNpm.exists()) {
                    log.success('更新模板成功')
                    this.templateNpm = templateNpm
                }

            }
        }


        // egg.js搭建一套后端系统

        // npm存储项目模板

        // 项目模板存储在mongodb数据库中

        // 通过egg.js获取mongodb中的数据并且通过api返回

    }

    async prepare() {
        // 判断项目模板是否存在
        const template = await getTemplate()
        if (!template || template.length === 0) {
            throw new Error('项目模板不存在')
        }
        this.template = template
            // 1.判断当前目录是否为空
            // 当前文件目录
        const localPath = process.cwd()
        if (!this.isDirEmpty(localPath)) {
            let ifContinue
            if (!this.force) {
                // 当前为目录为空
                // 询问是否继续创建
                ifContinue = (await inquirer.prompt([{
                    type: 'confirm',
                    name: 'ifContinue',
                    message: '当前文件夹不为空，是否继续创建项目',
                    default: false
                }])).ifContinue
                if (!ifContinue) {
                    return
                }
            }

            if (ifContinue || this.force) {
                // 2.是否启动强制更新
                // 二次确认
                const { confirmDelete } = await inquirer.prompt([{
                    type: 'confirm',
                    name: 'confirmDelete',
                    message: '是否确认清空当前目录下的文件',
                    default: false
                }])
                if (confirmDelete) {
                    // 清空当前目录
                    fse.emptyDirSync(localPath)
                }
            }
        }
        return this.getProjectInfo()
    }

    async getProjectInfo() {
        let projectInfo = null
            // 3.选择创建项目或组件
        const { type } = await inquirer.prompt([{
            type: 'list',
            name: 'type',
            message: '请选择初始化类型',
            default: TYPE_PROJECT,
            choices: [{
                name: '项目',
                value: TYPE_PROJECT
            }, {
                name: '组件',
                value: TYPE_COMPONENT
            }]
        }])

        // 获取项目信息
        if (type === TYPE_PROJECT) {
            const project = await inquirer.prompt([{
                type: 'input',
                name: 'projectName',
                message: '请输入项目名称',
                default: '',
                validate: function(val) {
                    // 项目名称校验
                    // 1、首字符必须为英文字符
                    // 2、尾字符必须为英文或数字，不能为字符
                    // 3、字符仅允许 - _
                    //
                    let result = /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(val)
                    const done = this.async()
                    setTimeout(() => {
                        if (!result) {
                            done('需要输入合法的项目名称')
                            return
                        }
                        done(null, true)
                    }, 0);
                    return
                },
                filter(val) {
                    return val
                }
            }, {
                type: 'input',
                name: 'projectVersion',
                message: '请输入项目版本号',
                default: '1.0.0',
                validate: function(val) {
                    const done = this.async()
                    setTimeout(() => {
                        if (!(!!semver.valid(val))) {
                            done('需要输入合法的项目版本')
                            return
                        }
                        done(null, true)
                    }, 0);

                    return
                },
                filter(val) {
                    if (!!semver.valid(val)) {
                        return semver.valid(val)
                    }
                    return val
                }
            }, {
                type: 'list',
                name: 'projectTemplate',
                message: '请选择项目模板',
                choices: this.createTemplateChoice()
            }])

            projectInfo = {
                type,
                ...project
            }

        } else if (type === TYPE_COMPONENT) {

        }

        log.verbose('list: ', type)

        // 对项目名称进行格式化
        if (projectInfo.projectName) {
            // 驼峰变 -
            projectInfo.className = kebab(projectInfo.projectName).replace(/^-/, '')
        }

        // 4.获取项目的基本信息
        return projectInfo

    }

    isDirEmpty(localPath) {

        let fileList = fs.readdirSync(localPath)
        log.verbose(fileList);
        // 过滤 文件列表
        let fileName = [
            '.DS_Store',
            '.git',
            '.gitignore',
            'node_modules',
            'package-lock.json',
            'package.json',
        ]
        fileList = fileList.filter(file => !file.startsWith('.') && !fileName.includes(file))
        return !fileList || fileList.length <= 0
    }

    createTemplateChoice() {
        return this.template.map(e => ({
            name: e.name,
            value: e.npmName
        }))
    }

}



function init(argv) {
    return new InitCommand(argv)
}

module.exports = init

module.exports.InitCommand = InitCommand